#include <aura.h>
#include "private.h"
#include <sys/types.h>
#include <aura_wchar.h>
#include "linknode.h"

static Aura_Handle s_hShMListLock;
static DLinkNode   s_sharedMemoryList;

#define LOCK    START_LOCK(s_hShMListLock)
#define UNLOCK  END_LOCK(s_hShMListLock)

struct SharedMemory : public DLinkNode
{
    Aura_WChar   m_name[AURA_MAX_PATH];
    Aura_Address m_address;
    Aura_Int32   m_refs;
};

Aura_ECode SharedMemory_Initialize()
{
    s_sharedMemoryList.Initialize();
    return AuraCriticalSection_Create(&s_hShMListLock);
}

Aura_Void  SharedMemory_Destroy()
{
    struct SharedMemory * pShM = NULL;

    while(!s_sharedMemoryList.IsEmpty()) {
        pShM = (struct SharedMemory *)s_sharedMemoryList.Next();
        pShM->Detach();
        Aura_free(pShM);
    }
    AuraCriticalSection_Destroy(s_hShMListLock);
}

AURA_API AuraSharedMemory_Create(
        /* [in] */ Aura_PWChar name,
        /* [in] */ Aura_MemorySize size,
        /* [in] */ Aura_Bool bCommit,
        /* [out] */ Aura_Handle * phSharedMemory,
        /* [out] */ Aura_Bool * pbAlreadyExisted)
{
    SharedMemory * pMemory = NULL;

#if defined(_arm)
    // Damm!!! ARM for the list will be called contructor.
    // we don't konw why yet.
    static Aura_Bool s_bListInitialized = AURA_FALSE;
    if (!s_bListInitialized) {
        s_bListInitialized = AURA_TRUE;
        s_sharedMemoryList.Initialize();
    }
#endif
    LOCK;
    ForEachDLinkNode(SharedMemory*, pMemory, &s_sharedMemoryList) {
        if (!Aura_Wcscmp(name, pMemory->m_name)) {
            pMemory->m_refs++;
            UNLOCK;

            *phSharedMemory   = (Aura_Handle)pMemory;
            *pbAlreadyExisted = AURA_TRUE;

            return AURA_NOERROR;
        }
    }

    pMemory = (struct SharedMemory *)Aura_malloc(sizeof(*pMemory) + size);
    if (NULL == pMemory) {
        UNLOCK;

        return AURA_E_OUT_OF_MEMORY;
    }
    Aura_Wcscpy(pMemory->m_name, name);
    pMemory->m_refs = 1;
    pMemory->Initialize();
    pMemory->m_address = (Aura_Address)(pMemory + 1);

    s_sharedMemoryList.InsertLast(pMemory);
    UNLOCK;

    *phSharedMemory   = (Aura_Handle)pMemory;
    *pbAlreadyExisted = AURA_FALSE;

     return AURA_NOERROR;
}


AURA_API AuraSharedMemory_Attach(
        /* [in] */ Aura_Handle hSharedMemory,
        /* [in] */ Aura_MemoryProtection protect,
        /* [out] */ Aura_Address * pAddress)
{
    struct SharedMemory * pShM = (struct SharedMemory *)hSharedMemory;

    assert(NULL != hSharedMemory);
    assert(NULL != pAddress);

    *pAddress = pShM->m_address;

    return AURA_NOERROR;
}


AURA_API AuraSharedMemory_Detach(
        /* [in] */ Aura_Handle hSharedMemory,
        /* [in] */ Aura_Address address)
{
    assert(NULL != hSharedMemory);
    // do nothing.
    return AURA_NOERROR;
}

AURA_API AuraSharedMemory_Destroy(
        /* [in] */ Aura_Handle hSharedMemory)
{
    struct SharedMemory * pShM = (struct SharedMemory *)hSharedMemory;
    assert(NULL != hSharedMemory);

    if (--pShM->m_refs > 0) {
        return AURA_NOERROR;
    }

    pShM->Detach();
    Aura_free(pShM);
    return AURA_NOERROR;
}
